/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*!
 Copyright (C) 2008 Florent Grenier

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
 */

/*  This example shows how to set up a term structure and then price
    some simple bonds. The last part is dedicated to peripherical
    computations such as "Yield to Price" or "Price to Yield"
 */

#include <ql/qldefines.hpp>
#include <ql/instruments/bonds/zerocouponbond.hpp>
#include <ql/instruments/bonds/floatingratebond.hpp>
#include <ql/pricingengines/bond/discountingbondengine.hpp>
#include <ql/cashflows/couponpricer.hpp>
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/yield/bondhelpers.hpp>
#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/indexes/ibor/usdlibor.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/thirty360.hpp>

#include <chrono>
#include <iostream>
#include <iomanip>

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

Integer sessionId() { return 0; }

}
#endif


int main(int, char *[]) {

    try {

        std::chrono::time_point<std::chrono::steady_clock> startT = std::chrono::steady_clock::now();

        std::cout << std::endl;

        /*********************
         ***  MARKET DATA  ***
         *********************/

        Calendar calendar = TARGET();

        Date settlementDate(18, September, 2008);
        // must be a business day
        settlementDate = calendar.adjust(settlementDate);

        Integer fixingDays = 3;
        Natural settlementDays = 3;

        Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days);
        // nothing to do with Date::todaysDate
        Settings::instance().evaluationDate() = todaysDate;

        std::cout << "Today: " << todaysDate.weekday()
                  << ", " << todaysDate << std::endl;

        std::cout << "Settlement date: " << settlementDate.weekday()
                  << ", " << settlementDate << std::endl;


        // Building of the bonds discounting yield curve

        /*********************
         ***  RATE HELPERS ***
         *********************/

        // RateHelpers are built from the above quotes together with
        // other instrument dependant infos.  Quotes are passed in
        // relinkable handles which could be relinked to some other
        // data source later.

        // Common data

        // ZC rates for the short end
        Rate zc3mQuote = 0.0096;
        Rate zc6mQuote = 0.0145;
        Rate zc1yQuote = 0.0194;

        std::shared_ptr < Quote > zc3mRate = std::make_shared<SimpleQuote>(zc3mQuote);
        std::shared_ptr < Quote > zc6mRate = std::make_shared<SimpleQuote>(zc6mQuote);
        std::shared_ptr < Quote > zc1yRate = std::make_shared<SimpleQuote>(zc1yQuote);

        DayCounter zcBondsDayCounter = Actual365Fixed();

        std::shared_ptr < RateHelper > zc3m = std::make_shared<DepositRateHelper>(
                Handle<Quote>(zc3mRate),
                3 * Months, fixingDays,
                calendar, ModifiedFollowing,
                true, zcBondsDayCounter);
        std::shared_ptr < RateHelper > zc6m = std::make_shared<DepositRateHelper>(
                Handle<Quote>(zc6mRate),
                6 * Months, fixingDays,
                calendar, ModifiedFollowing,
                true, zcBondsDayCounter);
        std::shared_ptr < RateHelper > zc1y = std::make_shared<DepositRateHelper>(
                Handle<Quote>(zc1yRate),
                1 * Years, fixingDays,
                calendar, ModifiedFollowing,
                true, zcBondsDayCounter);

        // setup bonds
        Real redemption = 100.0;

        const Size numberOfBonds = 5;

        Date issueDates[] = {
                Date(15, March, 2005),
                Date(15, June, 2005),
                Date(30, June, 2006),
                Date(15, November, 2002),
                Date(15, May, 1987)
        };

        Date maturities[] = {
                Date(31, August, 2010),
                Date(31, August, 2011),
                Date(31, August, 2013),
                Date(15, August, 2018),
                Date(15, May, 2038)
        };

        Real couponRates[] = {
                0.02375,
                0.04625,
                0.03125,
                0.04000,
                0.04500
        };

        Real marketQuotes[] = {
                100.390625,
                106.21875,
                100.59375,
                101.6875,
                102.140625
        };

        std::vector<std::shared_ptr<SimpleQuote> > quote;
        for (Size i = 0; i < numberOfBonds; i++) {
            std::shared_ptr < SimpleQuote > cp = std::make_shared<SimpleQuote>(marketQuotes[i]);
            quote.emplace_back(cp);
        }

        RelinkableHandle<Quote> quoteHandle[numberOfBonds];
        for (Size i = 0; i < numberOfBonds; i++) {
            quoteHandle[i].linkTo(quote[i]);
        }

        // Definition of the rate helpers
        std::vector<std::shared_ptr<BondHelper> > bondsHelpers;

        for (Size i = 0; i < numberOfBonds; i++) {

            Schedule schedule(issueDates[i], maturities[i], Period(Semiannual),
                              UnitedStates(UnitedStates::GovernmentBond),
                              Unadjusted, Unadjusted, DateGeneration::Backward, false);

            std::shared_ptr < FixedRateBondHelper > bondHelper = std::make_shared<FixedRateBondHelper>(
                    quoteHandle[i],
                    settlementDays,
                    100.0,
                    schedule,
                    std::vector<Rate>(1, couponRates[i]),
                    ActualActual(ActualActual::Bond),
                    Unadjusted,
                    redemption,
                    issueDates[i]);

            // the above could also be done by creating a
            // FixedRateBond instance and writing:
            //
            // std::shared_ptr<BondHelper> bondHelper =
            //         std::make_shared<BondHelper>(quoteHandle[i], bond);
            //
            // This would also work for bonds that still don't have a
            // specialized helper, such as floating-rate bonds.


            bondsHelpers.emplace_back(bondHelper);
        }

        /*********************
         **  CURVE BUILDING **
         *********************/

        // Any DayCounter would be fine.
        // ActualActual::ISDA ensures that 30 years is 30.0
        DayCounter termStructureDayCounter =
                ActualActual(ActualActual::ISDA);

        double tolerance = 1.0e-15;

        // A depo-bond curve
        std::vector<std::shared_ptr<RateHelper> > bondInstruments;

        // Adding the ZC bonds to the curve for the short end
        bondInstruments.emplace_back(zc3m);
        bondInstruments.emplace_back(zc6m);
        bondInstruments.emplace_back(zc1y);

        // Adding the Fixed rate bonds to the curve for the long end
        for (Size i = 0; i < numberOfBonds; i++) {
            bondInstruments.emplace_back(bondsHelpers[i]);
        }

        std::shared_ptr < YieldTermStructure > bondDiscountingTermStructure =
                std::make_shared<PiecewiseYieldCurve<Discount, LogLinear>>(
                        settlementDate, bondInstruments,
                        termStructureDayCounter,
                        tolerance);

        // Building of the Libor forecasting curve
        // deposits
        Rate d1wQuote = 0.043375;
        Rate d1mQuote = 0.031875;
        Rate d3mQuote = 0.0320375;
        Rate d6mQuote = 0.03385;
        Rate d9mQuote = 0.0338125;
        Rate d1yQuote = 0.0335125;
        // swaps
        Rate s2yQuote = 0.0295;
        Rate s3yQuote = 0.0323;
        Rate s5yQuote = 0.0359;
        Rate s10yQuote = 0.0412;
        Rate s15yQuote = 0.0433;


        /********************
         ***    QUOTES    ***
         ********************/

        // SimpleQuote stores a value which can be manually changed;
        // other Quote subclasses could read the value from a database
        // or some kind of data feed.

        // deposits
        std::shared_ptr < Quote > d1wRate = std::make_shared<SimpleQuote>(d1wQuote);
        std::shared_ptr < Quote > d1mRate = std::make_shared<SimpleQuote>(d1mQuote);
        std::shared_ptr < Quote > d3mRate = std::make_shared<SimpleQuote>(d3mQuote);
        std::shared_ptr < Quote > d6mRate = std::make_shared<SimpleQuote>(d6mQuote);
        std::shared_ptr < Quote > d9mRate = std::make_shared<SimpleQuote>(d9mQuote);
        std::shared_ptr < Quote > d1yRate = std::make_shared<SimpleQuote>(d1yQuote);
        // swaps
        std::shared_ptr < Quote > s2yRate = std::make_shared<SimpleQuote>(s2yQuote);
        std::shared_ptr < Quote > s3yRate = std::make_shared<SimpleQuote>(s3yQuote);
        std::shared_ptr < Quote > s5yRate = std::make_shared<SimpleQuote>(s5yQuote);
        std::shared_ptr < Quote > s10yRate = std::make_shared<SimpleQuote>(s10yQuote);
        std::shared_ptr < Quote > s15yRate = std::make_shared<SimpleQuote>(s15yQuote);

        /*********************
         ***  RATE HELPERS ***
         *********************/

        // RateHelpers are built from the above quotes together with
        // other instrument dependant infos.  Quotes are passed in
        // relinkable handles which could be relinked to some other
        // data source later.

        // deposits
        DayCounter depositDayCounter = Actual360();

        std::shared_ptr < RateHelper > d1w = std::make_shared<DepositRateHelper>(
                Handle<Quote>(d1wRate),
                1 * Weeks, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter);
        std::shared_ptr < RateHelper > d1m = std::make_shared<DepositRateHelper>(
                Handle<Quote>(d1mRate),
                1 * Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter);
        std::shared_ptr < RateHelper > d3m = std::make_shared<DepositRateHelper>(
                Handle<Quote>(d3mRate),
                3 * Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter);
        std::shared_ptr < RateHelper > d6m = std::make_shared<DepositRateHelper>(
                Handle<Quote>(d6mRate),
                6 * Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter);
        std::shared_ptr < RateHelper > d9m = std::make_shared<DepositRateHelper>(
                Handle<Quote>(d9mRate),
                9 * Months, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter);
        std::shared_ptr < RateHelper > d1y = std::make_shared<DepositRateHelper>(
                Handle<Quote>(d1yRate),
                1 * Years, fixingDays,
                calendar, ModifiedFollowing,
                true, depositDayCounter);

        // setup swaps
        Frequency swFixedLegFrequency = Annual;
        BusinessDayConvention swFixedLegConvention = Unadjusted;
        DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European);
        std::shared_ptr < IborIndex > swFloatingLegIndex = std::make_shared<Euribor6M>();

        const Period forwardStart(1 * Days);

        std::shared_ptr < RateHelper > s2y = std::make_shared<SwapRateHelper>(
                Handle<Quote>(s2yRate), 2 * Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(), forwardStart);
        std::shared_ptr < RateHelper > s3y = std::make_shared<SwapRateHelper>(
                Handle<Quote>(s3yRate), 3 * Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(), forwardStart);
        std::shared_ptr < RateHelper > s5y = std::make_shared<SwapRateHelper>(
                Handle<Quote>(s5yRate), 5 * Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(), forwardStart);
        std::shared_ptr < RateHelper > s10y = std::make_shared<SwapRateHelper>(
                Handle<Quote>(s10yRate), 10 * Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(), forwardStart);
        std::shared_ptr < RateHelper > s15y = std::make_shared<SwapRateHelper>(
                Handle<Quote>(s15yRate), 15 * Years,
                calendar, swFixedLegFrequency,
                swFixedLegConvention, swFixedLegDayCounter,
                swFloatingLegIndex, Handle<Quote>(), forwardStart);


        /*********************
         **  CURVE BUILDING **
         *********************/

        // Any DayCounter would be fine.
        // ActualActual::ISDA ensures that 30 years is 30.0

        // A depo-swap curve
        std::vector<std::shared_ptr<RateHelper> > depoSwapInstruments;
        depoSwapInstruments.emplace_back(d1w);
        depoSwapInstruments.emplace_back(d1m);
        depoSwapInstruments.emplace_back(d3m);
        depoSwapInstruments.emplace_back(d6m);
        depoSwapInstruments.emplace_back(d9m);
        depoSwapInstruments.emplace_back(d1y);
        depoSwapInstruments.emplace_back(s2y);
        depoSwapInstruments.emplace_back(s3y);
        depoSwapInstruments.emplace_back(s5y);
        depoSwapInstruments.emplace_back(s10y);
        depoSwapInstruments.emplace_back(s15y);
        std::shared_ptr < YieldTermStructure > depoSwapTermStructure =
                std::make_shared<PiecewiseYieldCurve<Discount, LogLinear>>(
                        settlementDate, depoSwapInstruments,
                        termStructureDayCounter,
                        tolerance);

        // Term structures that will be used for pricing:
        // the one used for discounting cash flows
        RelinkableHandle<YieldTermStructure> discountingTermStructure;
        // the one used for forward rate forecasting
        RelinkableHandle<YieldTermStructure> forecastingTermStructure;

        /*********************
         * BONDS TO BE PRICED *
         **********************/

        // Common data
        Real faceAmount = 100;

        // Pricing engine
        std::shared_ptr < PricingEngine > bondEngine =
                std::make_shared<DiscountingBondEngine>(discountingTermStructure);

        // Zero coupon bond
        ZeroCouponBond zeroCouponBond(
                settlementDays,
                UnitedStates(UnitedStates::GovernmentBond),
                faceAmount,
                Date(15, August, 2013),
                Following,
                Real(116.92),
                Date(15, August, 2003));

        zeroCouponBond.setPricingEngine(bondEngine);

        // Fixed 4.5% US Treasury Note
        Schedule fixedBondSchedule(Date(15, May, 2007),
                                   Date(15, May, 2017), Period(Semiannual),
                                   UnitedStates(UnitedStates::GovernmentBond),
                                   Unadjusted, Unadjusted, DateGeneration::Backward, false);

        FixedRateBond fixedRateBond(
                settlementDays,
                faceAmount,
                fixedBondSchedule,
                std::vector<Rate>(1, 0.045),
                ActualActual(ActualActual::Bond),
                ModifiedFollowing,
                100.0, Date(15, May, 2007));

        fixedRateBond.setPricingEngine(bondEngine);

        // Floating rate bond (3M USD Libor + 0.1%)
        // Should and will be priced on another curve later...

        RelinkableHandle<YieldTermStructure> liborTermStructure;
        const std::shared_ptr<IborIndex> libor3m =
                std::make_shared<USDLibor>(Period(3, Months), liborTermStructure);
        libor3m->addFixing(Date(17, July, 2008), 0.0278625);

        Schedule floatingBondSchedule(Date(21, October, 2005),
                                      Date(21, October, 2010), Period(Quarterly),
                                      UnitedStates(UnitedStates::NYSE),
                                      Unadjusted, Unadjusted, DateGeneration::Backward, true);

        FloatingRateBond floatingRateBond(
                settlementDays,
                faceAmount,
                floatingBondSchedule,
                libor3m,
                Actual360(),
                ModifiedFollowing,
                Natural(2),
                // Gearings
                std::vector<Real>(1, 1.0),
                // Spreads
                std::vector<Rate>(1, 0.001),
                // Caps
                std::vector<Rate>(),
                // Floors
                std::vector<Rate>(),
                // Fixing in arrears
                true,
                Real(100.0),
                Date(21, October, 2005));

        floatingRateBond.setPricingEngine(bondEngine);

        // Coupon pricers
        std::shared_ptr < IborCouponPricer > pricer = std::make_shared<BlackIborCouponPricer>();

        // optionLet volatilities
        Volatility volatility = 0.0;
        Handle<OptionletVolatilityStructure> vol;
        vol = Handle<OptionletVolatilityStructure>(
                std::make_shared<ConstantOptionletVolatility>(
                        settlementDays,
                        calendar,
                        ModifiedFollowing,
                        volatility,
                        Actual365Fixed()));

        pricer->setCapletVolatility(vol);
        setCouponPricer(floatingRateBond.cashflows(), pricer);

        // Yield curve bootstrapping
        forecastingTermStructure.linkTo(depoSwapTermStructure);
        discountingTermStructure.linkTo(bondDiscountingTermStructure);

        // We are using the depo & swap curve to estimate the future Libor rates
        liborTermStructure.linkTo(depoSwapTermStructure);

        /***************
         * BOND PRICING *
         ****************/

        std::cout << std::endl;

        // write column headings
        Size widths[] = {18, 10, 10, 10};

        std::cout << std::setw(widths[0]) << "                 "
                  << std::setw(widths[1]) << "ZC"
                  << std::setw(widths[2]) << "Fixed"
                  << std::setw(widths[3]) << "Floating"
                  << std::endl;

        Size width = widths[0] + widths[1] + widths[2] + widths[3];
        std::string rule(width, '-');

        std::cout << rule << std::endl;

        std::cout << std::fixed;
        std::cout << std::setprecision(2);

        std::cout << std::setw(widths[0]) << "Net present value"
                  << std::setw(widths[1]) << zeroCouponBond.NPV()
                  << std::setw(widths[2]) << fixedRateBond.NPV()
                  << std::setw(widths[3]) << floatingRateBond.NPV()
                  << std::endl;

        std::cout << std::setw(widths[0]) << "Clean price"
                  << std::setw(widths[1]) << zeroCouponBond.cleanPrice()
                  << std::setw(widths[2]) << fixedRateBond.cleanPrice()
                  << std::setw(widths[3]) << floatingRateBond.cleanPrice()
                  << std::endl;

        std::cout << std::setw(widths[0]) << "Dirty price"
                  << std::setw(widths[1]) << zeroCouponBond.dirtyPrice()
                  << std::setw(widths[2]) << fixedRateBond.dirtyPrice()
                  << std::setw(widths[3]) << floatingRateBond.dirtyPrice()
                  << std::endl;

        std::cout << std::setw(widths[0]) << "Accrued coupon"
                  << std::setw(widths[1]) << zeroCouponBond.accruedAmount()
                  << std::setw(widths[2]) << fixedRateBond.accruedAmount()
                  << std::setw(widths[3]) << floatingRateBond.accruedAmount()
                  << std::endl;

        std::cout << std::setw(widths[0]) << "Previous coupon"
                  << std::setw(widths[1]) << "N/A" // zeroCouponBond
                  << std::setw(widths[2]) << io::rate(fixedRateBond.previousCouponRate())
                  << std::setw(widths[3]) << io::rate(floatingRateBond.previousCouponRate())
                  << std::endl;

        std::cout << std::setw(widths[0]) << "Next coupon"
                  << std::setw(widths[1]) << "N/A" // zeroCouponBond
                  << std::setw(widths[2]) << io::rate(fixedRateBond.nextCouponRate())
                  << std::setw(widths[3]) << io::rate(floatingRateBond.nextCouponRate())
                  << std::endl;

        std::cout << std::setw(widths[0]) << "Yield"
                  << std::setw(widths[1])
                  << io::rate(zeroCouponBond.yield(Actual360(), Compounded, Annual))
                  << std::setw(widths[2])
                  << io::rate(fixedRateBond.yield(Actual360(), Compounded, Annual))
                  << std::setw(widths[3])
                  << io::rate(floatingRateBond.yield(Actual360(), Compounded, Annual))
                  << std::endl;

        std::cout << std::endl;

        // Other computations
        std::cout << "Sample indirect computations (for the floating rate bond): " << std::endl;
        std::cout << rule << std::endl;

        std::cout << "Yield to Clean Price: "
                  << floatingRateBond.cleanPrice(floatingRateBond.yield(Actual360(), Compounded, Annual), Actual360(),
                                                 Compounded, Annual, settlementDate) << std::endl;

        std::cout << "Clean Price to Yield: "
                  << io::rate(floatingRateBond.yield(floatingRateBond.cleanPrice(), Actual360(), Compounded, Annual,
                                                     settlementDate)) << std::endl;

        /* "Yield to Price"
           "Price to Yield" */

        std::chrono::time_point<std::chrono::steady_clock> endT = std::chrono::steady_clock::now();
        double elapsed = static_cast<double>((endT - startT).count()) / 1.0e9;

        double seconds = elapsed;
        Integer hours = int(seconds / 3600);
        seconds -= hours * 3600;
        Integer minutes = int(seconds / 60);
        seconds -= minutes * 60;
        std::cout << " \nRun completed in ";
        if (hours > 0)
            std::cout << hours << " h ";
        if (hours > 0 || minutes > 0)
            std::cout << minutes << " m ";
        std::cout << std::fixed << std::setprecision(0)
                  << seconds << " s\n" << std::endl;

        return 0;

    } catch (std::exception &e) {
        std::cerr << e.what() << std::endl;
        return 1;
    } catch (...) {
        std::cerr << "unknown error" << std::endl;
        return 1;
    }
}

